home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Newswatcher 2.0b22 / NW Source / Source / fileutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-15  |  20.0 KB  |  774 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     fileutil.c
  4.  
  5.     This reusable module contains miscellaneous file management utility routines.
  6.     
  7.     Copyright © 1994, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <Folders.h>
  12. #include <Script.h>
  13. #include <Aliases.h>
  14. #include <stdio.h>
  15. #include <string.h>
  16.  
  17. #include "def.h"
  18. #include "fileutil.h"
  19. #include "strutil.h"
  20. #include "memutil.h"
  21.  
  22.  
  23.  
  24. /*----------------------------------------------------------------------------
  25.     IsEqualFSSpec 
  26.     
  27.     Compare two canonical FSSpec records.
  28.             
  29.     Entry:    file1 = pointer to first FSSpec record.
  30.             file2 = pointer to second FSSpec record.
  31.     
  32.     Exit:    function result = true if the FSSpec records are equal.
  33. ----------------------------------------------------------------------------*/
  34.  
  35. Boolean IsEqualFSSpec (FSSpec *file1, FSSpec *file2)
  36. {
  37.     return
  38.         file1->vRefNum == file2->vRefNum &&
  39.         file1->parID == file2->parID &&
  40.         EqualString(file1->name, file2->name, false, true);
  41. }
  42.  
  43.  
  44.  
  45. /*----------------------------------------------------------------------------
  46.     VolNameToVRefNum 
  47.     
  48.     Get the volume reference number given a volume name.
  49.             
  50.     Entry:    name = volume name.
  51.     
  52.     Exit:    function result = error code.
  53.             *vRefNum = volume reference number.
  54. ----------------------------------------------------------------------------*/
  55.  
  56. OSErr VolNameToVRefNum (StringPtr name, short *vRefNum)
  57. {
  58.     HParamBlockRec pb;
  59.     Str31 volNameWithColon;
  60.     short len;
  61.     OSErr err = noErr;
  62.     
  63.     CopyPascalString(volNameWithColon, name);
  64.     len = *volNameWithColon;
  65.     if (volNameWithColon[len] != ':') {
  66.         len = ++(*volNameWithColon);
  67.         volNameWithColon[len] = ':';
  68.     }
  69.     pb.volumeParam.ioNamePtr = volNameWithColon;
  70.     pb.volumeParam.ioVolIndex = -1;
  71.     err = PBHGetVInfoSync(&pb);
  72.     *vRefNum = pb.volumeParam.ioVRefNum;
  73.     return err;
  74. }
  75.  
  76.  
  77.  
  78. /*----------------------------------------------------------------------------
  79.     VolNameAndCreationDateToVRefNum 
  80.     
  81.     Get the volume reference number given a volume name and creation date.
  82.             
  83.     Entry:    name = volume name.
  84.             crDate = creation date.
  85.     
  86.     Exit:    function result = error code.
  87.             *vRefNum = volume reference number.
  88. ----------------------------------------------------------------------------*/
  89.  
  90. OSErr VolNameAndCreationDateToVRefNum (StringPtr name, long crDate, short *vRefNum)
  91. {
  92.     HParamBlockRec pb;
  93.     Str31 volName, volNameWithColon;
  94.     short len, ioVolIndex;
  95.     OSErr err = noErr;
  96.  
  97.     for (ioVolIndex = 1; ; ioVolIndex++) {
  98.         pb.volumeParam.ioNamePtr = volName;
  99.         pb.volumeParam.ioVolIndex = ioVolIndex;
  100.         err = PBHGetVInfoSync(&pb);
  101.         if (err != noErr) return err;
  102.         CopyPascalString(volNameWithColon, volName);
  103.         len = *volNameWithColon;
  104.         if (volNameWithColon[len] == ':') {
  105.             (*volName)--;
  106.         } else {
  107.             len = ++(*volNameWithColon);
  108.             volNameWithColon[len] = ':';
  109.         }
  110.         if (EqualString(name, volName, false, true) || 
  111.             EqualString(name, volNameWithColon, false, true))
  112.         {
  113.             if (pb.volumeParam.ioVCrDate == crDate) {
  114.                 *vRefNum = pb.volumeParam.ioVRefNum;
  115.                 return noErr;
  116.             }
  117.         }
  118.     }
  119. }
  120.  
  121.  
  122.  
  123. /*----------------------------------------------------------------------------
  124.     CreateTemporaryFile 
  125.     
  126.     Create a temporary file.
  127.  
  128.     Entry:    prefix = 4 character prefix for file name (e.g., caller's
  129.                 creator code).
  130.             creator = file creator.
  131.             type = file type.
  132.             
  133.     Exit:    function result = error code.
  134.             *fSpec = FSSpec record for new temporary file.
  135. ----------------------------------------------------------------------------*/
  136.  
  137. OSErr CreateTemporaryFile (FSSpec *fSpec, OSType prefix, OSType creator, OSType type)
  138. {
  139.     OSErr err = noErr;
  140.     long ticks;
  141.  
  142.     err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder,
  143.         &fSpec->vRefNum, &fSpec->parID);
  144.     if (err != noErr) return err;
  145.     ticks = TickCount();
  146.     while (true) {
  147.         sprintf((char*)fSpec->name, "%.4s-tmp-%ld", &prefix, ticks);
  148.         c2pstr((char*)fSpec->name);
  149.         err = FSpCreate(fSpec, creator, type, smSystemScript);
  150.         if (err == noErr) return noErr;
  151.         if (err != dupFNErr) return err;
  152.         ticks++;
  153.     }
  154.     return noErr;
  155. }
  156.  
  157.  
  158.  
  159. /*----------------------------------------------------------------------------
  160.     DeleteTemporaryFiles 
  161.     
  162.     Delete all temporary files.
  163.  
  164.     Entry:    prefix = 4 character prefix for file name (e.g., caller's
  165.                 creator code).
  166.             
  167.     Exit:    function result = error code.
  168. ----------------------------------------------------------------------------*/
  169.  
  170. OSErr DeleteTemporaryFiles (OSType prefix)
  171. {
  172.     CInfoPBRec pBlock;
  173.     short vRefNum;
  174.     long dirID;
  175.     OSErr err = noErr;
  176.     Str31 fName;
  177.     short i;
  178.  
  179.     err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kDontCreateFolder,
  180.         &vRefNum, &dirID);
  181.     if (err != noErr) return noErr;
  182.     
  183.     i = 1;
  184.     while (true) {
  185.         pBlock.hFileInfo.ioVRefNum = vRefNum;
  186.         pBlock.hFileInfo.ioDirID = dirID;
  187.         pBlock.hFileInfo.ioNamePtr = fName;
  188.         pBlock.hFileInfo.ioFDirIndex = i;
  189.         err = PBGetCatInfoSync(&pBlock);
  190.         if (err != noErr) return noErr;
  191.         if (((pBlock.hFileInfo.ioFlAttrib >> 4) & 1) == 0) {
  192.             if (*fName >= 4 && strncmp((char*)fName+1, (char*)&prefix, 4) == 0) {
  193.                 HDelete(vRefNum, dirID, fName);
  194.             }
  195.         }
  196.         i++;
  197.     }
  198. }
  199.  
  200.  
  201.  
  202. /*----------------------------------------------------------------------------
  203.     GetSysVolume 
  204.     
  205.     Get the volume reference number of the system volume.
  206.             
  207.     Exit:    function result = error code.
  208.             *vRefNum = vol ref num of system volume.
  209. ----------------------------------------------------------------------------*/
  210.  
  211. OSErr GetSysVolume (short *vRefNum)
  212. {
  213.     long dir;
  214.     
  215.     return FindFolder(kOnSystemDisk, kSystemFolderType, false, vRefNum, &dir);
  216. }
  217.  
  218.  
  219.  
  220. /*----------------------------------------------------------------------------
  221.     GetIndVolume 
  222.     
  223.     Get a volume reference number by volume index.
  224.     
  225.     Entry:    index = volume index
  226.             
  227.     Exit:    function result = error code.
  228.             *vRefNum = vol ref num of indexed volume.
  229. ----------------------------------------------------------------------------*/
  230.  
  231. OSErr GetIndVolume (short index, short *vRefNum)
  232. {
  233.     ParamBlockRec pb;
  234.     OSErr err = noErr;
  235.     
  236.     pb.volumeParam.ioCompletion = nil;
  237.     pb.volumeParam.ioNamePtr = nil;
  238.     pb.volumeParam.ioVolIndex = index;
  239.     
  240.     err = PBGetVInfoSync(&pb);
  241.     
  242.     *vRefNum = pb.volumeParam.ioVRefNum;
  243.     return err;
  244. }
  245.  
  246.  
  247.  
  248. /*----------------------------------------------------------------------------
  249.     MakeLegalFileName
  250.  
  251.     Make a legal file name from a string.
  252.     
  253.     Entry:    str = P-format string.
  254.             
  255.     Exit:    fileName = P-format file name.
  256.     
  257.     The string is truncated to 31 characters. Any leading period is replaced
  258.     by an underscore (_). Any embedded slashes or colons or CRs are replaced by ' '.
  259.     If the string is empty, the file name "x" is returned.
  260. ----------------------------------------------------------------------------*/
  261.  
  262. void MakeLegalFileName (StringPtr str, Str31 fileName)
  263. {
  264.     short i, len;
  265.  
  266.     len = str[0];
  267.     if (len == 0) {
  268.         CopyPascalString(fileName, "\px");
  269.         return;
  270.     }
  271.     if (len > 31) len = 31;
  272.     fileName[0] = len;
  273.     BlockMoveData(str+1, fileName+1, len);
  274.     if (fileName[1] == '.') fileName[1] = '_';
  275.     for (i = 1; i <= len; i++)
  276.         if (fileName[i] == '/' || fileName[i] == ':' || fileName[i] == CR) fileName[i] = ' ';
  277. }
  278.  
  279.  
  280.  
  281. /*----------------------------------------------------------------------------
  282.     OpenDataForkWriteCreateIfMissing
  283.     
  284.     Open the data fork of a file for writing. Create the file if it is missing..
  285.     
  286.     Entry:    fSpec = pointer to file spec.
  287.             creator = creator code.
  288.             fileType = file type.
  289.             scriptTag = script code.
  290.             append = true to open for appending data, false to open for
  291.                 rewriting data.
  292.     
  293.     Exit:    function result = error code.
  294.             *refNum = file reference number.
  295.             *empty = true if file is empty, false if append was requested
  296.                 and existing file was not empty.
  297. ----------------------------------------------------------------------------*/
  298.  
  299. OSErr OpenDataForkWriteCreateIfMissing (FSSpec *fSpec, OSType creator, OSType fileType, 
  300.     ScriptCode scriptTag, Boolean append, short *refNum, Boolean *empty)
  301. {
  302.     short fRefNum = 0;
  303.     long eof;
  304.     OSErr err = noErr;
  305.  
  306.     *empty = true;
  307.     err = FSpOpenDF(fSpec, fsRdWrPerm, &fRefNum);
  308.     if (err == noErr) {
  309.         if (append) {
  310.             err = GetEOF(fRefNum, &eof);
  311.             if (err != noErr) goto exit;
  312.             if (eof != 0) {
  313.                 *empty = false;
  314.                 err = SetFPos(fRefNum, fsFromLEOF, 0);
  315.                 if (err != noErr) goto exit;
  316.             }
  317.         } else {
  318.             err = SetEOF(fRefNum, 0);
  319.             if (err != noErr) goto exit;
  320.         }
  321.     } else if (err == fnfErr) {
  322.         err = FSpCreate(fSpec, creator, fileType, scriptTag);
  323.         if (err != noErr) goto exit;
  324.         err = FSpOpenDF(fSpec, fsRdWrPerm, &fRefNum);
  325.         if (err != noErr) goto exit;
  326.     } else {
  327.         goto exit;
  328.     }
  329.     
  330.     *refNum = fRefNum;
  331.     return noErr;
  332.     
  333. exit:
  334.  
  335.     if (fRefNum != 0) MyFSClose(fRefNum, nil);
  336.     return err;
  337. }
  338.  
  339.  
  340.  
  341. /*----------------------------------------------------------------------------
  342.     ValidateSavedFolderAlias
  343.     
  344.     Validate a saved folder alias.
  345.     
  346.     Entry:    alias = handle to alias.
  347.     
  348.     Exit:    *vRefNum = volume reference number of saved folder.
  349.             *dirID = directory ID of saved folder.
  350.             *valid = true if saved folder valid.
  351. ----------------------------------------------------------------------------*/
  352.  
  353. void ValidateSavedFolderAlias (AliasHandle alias, short *vRefNum, long *dirID, 
  354.     Boolean *valid)
  355. {
  356.     OSErr err = noErr;
  357.     FSSpec fSpec;
  358.     Boolean wasChanged;
  359.     CInfoPBRec pb;    
  360.     
  361.     *valid = false;
  362.     if (alias == nil) return;
  363.     err = ResolveAlias(nil, alias, &fSpec, &wasChanged);
  364.     if (err != noErr) return;
  365.     pb.dirInfo.ioNamePtr = fSpec.name;
  366.     pb.dirInfo.ioVRefNum = fSpec.vRefNum;
  367.     pb.dirInfo.ioFDirIndex = 0;
  368.     pb.dirInfo.ioDrDirID = fSpec.parID;
  369.     err = PBGetCatInfo(&pb, false);
  370.     if (err != noErr) return;
  371.     if (((pb.dirInfo.ioFlAttrib >> 4) & 1) == 0) return;
  372.     *vRefNum = pb.dirInfo.ioVRefNum;
  373.     *dirID = pb.dirInfo.ioDrDirID;
  374.     *valid = true;
  375. }
  376.  
  377.  
  378.  
  379. /*----------------------------------------------------------------------------
  380.     SearchFolderByCreatorAndType
  381.     
  382.     Search a folder for a file by creator and type.
  383.     
  384.     Entry:    fSpec = pointer to file spec with vRefNum and parID filled in
  385.                 for the folder to be searched.
  386.             creator = creator code.
  387.             fileType = file type.
  388.             *index = starting index in folder. Pass 1 to search the entire
  389.                 folder.
  390.     
  391.     Exit:    function result = error code.
  392.                 = fnfErr if file not found.
  393.             *fSpec = file spec of located file in folder.
  394.             *index = index of located file in folder.
  395. ----------------------------------------------------------------------------*/
  396.  
  397. OSErr SearchFolderByCreatorAndType (FSSpec *fSpec, OSType creator, OSType fileType,
  398.     short *index)
  399. {
  400.     CInfoPBRec pBlock;
  401.     FInfo fndrInfo;
  402.     OSErr err = noErr;
  403.     short i;
  404.     
  405.     i = *index;
  406.     while (true) {
  407.         pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  408.         pBlock.hFileInfo.ioDirID = fSpec->parID;
  409.         pBlock.hFileInfo.ioNamePtr = fSpec->name;
  410.         pBlock.hFileInfo.ioFDirIndex = i;
  411.         err = PBGetCatInfoSync(&pBlock);
  412.         if (err != noErr) return fnfErr;
  413.         if (((pBlock.hFileInfo.ioFlAttrib >> 4) & 1) == 0) {
  414.             err = FSpGetFInfo(fSpec, &fndrInfo);
  415.             if (err != noErr) return err;
  416.             if (fndrInfo.fdCreator == creator && fndrInfo.fdType == fileType) {
  417.                 *index = i;
  418.                 return noErr;
  419.             }
  420.         }
  421.         i++;
  422.     }
  423. }
  424.  
  425.  
  426.  
  427. /*----------------------------------------------------------------------------
  428.     FileOrFolderExists
  429.     
  430.     Check to see if a file or folder with a given name exists in a
  431.     directory.
  432.     
  433.     Entry:    fSpec = pointer to file spec.
  434.     
  435.     Exit:    function result = error code
  436.                 = fnfErr if file or folder doesn't exist.
  437.                 = noErr if file or folder does exist.
  438.                 = something else if error.
  439. ----------------------------------------------------------------------------*/
  440.  
  441. OSErr FileOrFolderExists (FSSpec *fSpec)
  442. {
  443.     OSErr err = noErr;
  444.     CInfoPBRec pBlock;
  445.     
  446.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  447.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  448.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  449.     pBlock.hFileInfo.ioFDirIndex = 0;
  450.     return PBGetCatInfoSync(&pBlock);
  451. }
  452.  
  453.  
  454.  
  455. /*----------------------------------------------------------------------------
  456.     MakeFileNameUnique
  457.     
  458.     Generate a unique file name in a directory.
  459.     
  460.     Entry:    fSpec = pointer to file spec without suffix.
  461.             suffix = pointer to suffix string, or nil if none.
  462.     
  463.     Exit:    function result = error code.
  464.             *fSpec = file spec with unique name.
  465. ----------------------------------------------------------------------------*/
  466.  
  467. OSErr MakeFileNameUnique (FSSpec *fSpec, char *suffix)
  468. {
  469.     short n, stubLen, suffixLen, digitsLen;
  470.     char fileNameStub[32];
  471.     char digitsStr[32];
  472.     char *suffixStr;
  473.     OSErr err = noErr;
  474.     
  475.     CopyPascalString((StringPtr)fileNameStub, fSpec->name);
  476.     p2cstr((StringPtr)fileNameStub);
  477.     suffixStr = suffix == nil ? "" : suffix;
  478.     stubLen = strlen(fileNameStub);
  479.     suffixLen = strlen(suffixStr);
  480.     if (stubLen + suffixLen > 31) {
  481.         stubLen = 31 - suffixLen;
  482.         fileNameStub[stubLen] = 0;
  483.     }
  484.     n = 1;
  485.     sprintf((char*)fSpec->name, "%s%s", fileNameStub, suffixStr);
  486.     c2pstr((char*)fSpec->name);
  487.     while (true) {
  488.         err = FileOrFolderExists(fSpec);
  489.         if (err == fnfErr) return noErr;
  490.         if (err != noErr) return err;
  491.         n++;
  492.         sprintf(digitsStr, ".%d", n);
  493.         digitsLen = strlen(digitsStr);
  494.         if (stubLen + digitsLen + suffixLen > 31) {
  495.             stubLen = 31 - suffixLen - digitsLen;
  496.             fileNameStub[stubLen] = 0;
  497.         }
  498.         sprintf((char*)fSpec->name, "%s%s%s", fileNameStub, digitsStr, suffixStr);
  499.         c2pstr((char*)fSpec->name);
  500.     }
  501. }
  502.  
  503.  
  504.  
  505. /*----------------------------------------------------------------------------
  506.     MyFSWriteNoCache
  507.     
  508.     Write data to a file with no caching.
  509.     
  510.     Entry:    fRefNum = file reference number.
  511.             inOutCount = pointer to number of bytes to write.
  512.             buffer = pointer to data to write.
  513.             giveTime = pointer to function to give time to other
  514.                 processes during the write, or nil if none.
  515.     
  516.     Exit:    function result = error code.
  517.             *inOutCount = number of bytes written.
  518. ----------------------------------------------------------------------------*/
  519.  
  520. OSErr MyFSWriteNoCache (short fRefNum, long *inOutCount, Ptr buffer,
  521.     OSErr (*giveTime)(Boolean))
  522. {
  523.     ParamBlockRec pBlock;
  524.     OSErr err = noErr;
  525.     
  526.     pBlock.ioParam.ioCompletion = nil;
  527.     pBlock.ioParam.ioResult = 1;
  528.     pBlock.ioParam.ioRefNum = fRefNum;
  529.     pBlock.ioParam.ioBuffer = buffer;
  530.     pBlock.ioParam.ioReqCount = *inOutCount;
  531.     pBlock.ioParam.ioPosMode = 0x20;            /* bit 5 set = no cache */
  532.     pBlock.ioParam.ioPosOffset = 0;
  533.     err = PBWriteAsync(&pBlock);
  534.     if (err != noErr) return err;
  535.     do {
  536.         if (err == noErr && giveTime != nil) err = (*giveTime)(true);
  537.     } while (pBlock.ioParam.ioResult > 0);
  538.     *inOutCount = pBlock.ioParam.ioActCount;
  539.     if (err == noErr) err = pBlock.ioParam.ioResult;
  540.     return err;
  541. }
  542.  
  543.  
  544.  
  545. /*----------------------------------------------------------------------------
  546.     GetFileVolRefNum
  547.     
  548.     Get the volume reference number of an open file.
  549.     
  550.     Entry:    fRefNum = file reference number.
  551.     
  552.     Exit:    function result = error code.
  553.             *vRefNum = volume reference number.
  554. ----------------------------------------------------------------------------*/
  555.  
  556. OSErr GetFileVolRefNum (short fRefNum, short *vRefNum)
  557. {
  558.     FCBPBRec pBlock;
  559.     OSErr err = noErr;
  560.     
  561.     pBlock.ioRefNum = fRefNum;
  562.     pBlock.ioFCBIndx = 0;
  563.     pBlock.ioNamePtr = nil;
  564.     err = PBGetFCBInfoSync(&pBlock);
  565.     if (err != noErr) return err;
  566.     *vRefNum = pBlock.ioVRefNum;
  567.     return noErr;
  568. }
  569.  
  570.  
  571.  
  572. /*----------------------------------------------------------------------------
  573.     MyFSClose
  574.     
  575.     Close an open file.
  576.     
  577.     Entry:    fRefNum = file reference number.
  578.             giveTime = pointer to function to give time to other
  579.                 processes during the close, or nil if none.
  580.     
  581.     Exit:    function result = error code.
  582. ----------------------------------------------------------------------------*/
  583.  
  584. OSErr MyFSClose (short fRefNum, OSErr (*giveTime)(Boolean))
  585. {
  586.     ParamBlockRec pBlock;
  587.     OSErr err = noErr;
  588.     short vRefNum;
  589.     
  590.     err = GetFileVolRefNum(fRefNum, &vRefNum);
  591.     if (err != noErr) return err;
  592.     
  593.     pBlock.ioParam.ioCompletion = nil;
  594.     pBlock.ioParam.ioResult = 1;
  595.     pBlock.ioParam.ioRefNum = fRefNum;
  596.     err = PBCloseAsync(&pBlock);
  597.     if (err != noErr) return err;
  598.     do {
  599.         if (err == noErr && giveTime != nil) err = (*giveTime)(true);
  600.     } while (pBlock.ioParam.ioResult > 0);
  601.     if (err == noErr) err = pBlock.ioParam.ioResult;
  602.     if (err != noErr) return err;
  603.     err = FlushVol(nil, vRefNum);
  604.     return err;
  605. }
  606.  
  607.  
  608.  
  609. /*----------------------------------------------------------------------------
  610.     GetLastModDateTime
  611.     
  612.     Get the last mod date and time of a file.
  613.     
  614.     Entry:    fSpec = pointer to file spec.
  615.     
  616.     Exit:    function result = error code.
  617.             *lastModDateTime = last mod date and time.
  618. ----------------------------------------------------------------------------*/
  619.  
  620. OSErr GetLastModDateTime(FSSpec *fSpec, unsigned long *lastModDateTime)
  621. {
  622.     CInfoPBRec pBlock;
  623.     OSErr err = noErr;
  624.     
  625.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  626.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  627.     pBlock.hFileInfo.ioFDirIndex = 0;
  628.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  629.     err = PBGetCatInfoSync(&pBlock);
  630.     if (err != noErr) return err;
  631.     *lastModDateTime = pBlock.hFileInfo.ioFlMdDat;
  632.     return noErr;
  633. }
  634.  
  635.  
  636.  
  637. /*----------------------------------------------------------------------------
  638.     SetLastModDateTime
  639.     
  640.     Set the last mod date and time of a file.
  641.     
  642.     Entry:    fSpec = pointer to file spec.
  643.             lastModDateTime = last mod date and time.
  644.     
  645.     Exit:    function result = error code.
  646. ----------------------------------------------------------------------------*/
  647.  
  648. OSErr SetLastModDateTime(FSSpec *fSpec, unsigned long lastModDateTime)
  649. {
  650.     CInfoPBRec pBlock;
  651.     OSErr err = noErr;
  652.     
  653.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  654.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  655.     pBlock.hFileInfo.ioFDirIndex = 0;
  656.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  657.     err = PBGetCatInfoSync(&pBlock);
  658.     if (err != noErr) return err;
  659.     pBlock.hFileInfo.ioNamePtr = fSpec->name;
  660.     pBlock.hFileInfo.ioVRefNum = fSpec->vRefNum;
  661.     pBlock.hFileInfo.ioFDirIndex = 0;
  662.     pBlock.hFileInfo.ioDirID = fSpec->parID;
  663.     pBlock.hFileInfo.ioFlMdDat = lastModDateTime;
  664.     err = PBSetCatInfoSync(&pBlock);
  665.     if (err != noErr) return err;
  666.     return noErr;
  667. }
  668.  
  669.  
  670.  
  671. /*----------------------------------------------------------------------------
  672.     CopyOneFork
  673.     
  674.     Copy one fork of a file.
  675.     
  676.     Entry:    source = pointer to source file spec.
  677.             dest = pointer to destination file spec.
  678.             resourceFork = true to copy resource fork, false to copy
  679.                 data fork.
  680.     
  681.     Exit:    function result = error code.
  682. ----------------------------------------------------------------------------*/
  683.  
  684. static OSErr CopyOneFork (FSSpec *source, FSSpec *dest, Boolean resourceFork)
  685. {
  686.     short sourceRefNum = 0;
  687.     short destRefNum = 0;
  688.     long fileSize, len;
  689.     Ptr buf;
  690.     FInfo fInfo;
  691.     OSErr err = noErr;
  692.     
  693.     /* Open source fork. */
  694.     
  695.     if (resourceFork) {
  696.         err = FSpOpenRF(source, fsRdPerm, &sourceRefNum);
  697.     } else {
  698.         err = FSpOpenDF(source, fsRdPerm, &sourceRefNum);
  699.     }
  700.     if (err == fnfErr) return noErr;
  701.     if (err != noErr) goto exit;
  702.     err = GetEOF(sourceRefNum, &fileSize);
  703.     if (err != noErr) goto exit;
  704.     
  705.     /* Open destination fork. Create the fork if it is missing. */
  706.     
  707.     if (resourceFork) {
  708.         err = FSpOpenRF(dest, fsRdWrPerm, &destRefNum);
  709.     } else {
  710.         err = FSpOpenDF(dest, fsRdWrPerm, &destRefNum);
  711.     }
  712.     if (err == fnfErr) {
  713.         err = FSpGetFInfo(source, &fInfo);
  714.         if (err != noErr) goto exit;
  715.         if (resourceFork) {
  716.             FSpCreateResFile(dest, fInfo.fdCreator, fInfo.fdType, smSystemScript);
  717.             err = ResError();
  718.         } else {
  719.             err = FSpCreate(dest, fInfo.fdCreator, fInfo.fdType, smSystemScript);
  720.         }
  721.         if (err != noErr) goto exit;
  722.         if (resourceFork) {
  723.             err = FSpOpenRF(dest, fsRdWrPerm, &destRefNum);
  724.         } else {
  725.             err = FSpOpenDF(dest, fsRdWrPerm, &destRefNum);
  726.         }
  727.     }
  728.     if (err != noErr) goto exit;
  729.     err = SetFPos(destRefNum, fsFromStart, 0);
  730.     if (err != noErr) goto exit;
  731.     
  732.     /* Copy the source fork to the destination fork. */
  733.     
  734.     err = MyNewPtr(1024, &buf);
  735.     if (err != noErr) goto exit;
  736.     while (fileSize > 0) {
  737.         len = fileSize > 1024 ? 1024 : fileSize;
  738.         err = FSRead(sourceRefNum, &len, buf);
  739.         if (err != noErr) goto exit;
  740.         err = FSWrite(destRefNum, &len, buf);
  741.         if (err != noErr) goto exit;
  742.         fileSize -= len;
  743.     }
  744.     
  745. exit:
  746.  
  747.     if (sourceRefNum != 0) MyFSClose(sourceRefNum, nil);
  748.     if (destRefNum != 0) MyFSClose(destRefNum, nil);
  749.     if (buf != nil) MyDisposePtr(buf);
  750.     return err;
  751. }
  752.  
  753.  
  754.  
  755. /*----------------------------------------------------------------------------
  756.     CopyFile
  757.     
  758.     Make a copy of a file (both forks).
  759.     
  760.     Entry:    source = pointer to source file spec.
  761.             dest = pointer to destination file spec.
  762.     
  763.     Exit:    function result = error code.
  764. ----------------------------------------------------------------------------*/
  765.  
  766. OSErr CopyFile (FSSpec *source, FSSpec *dest)
  767. {
  768.     OSErr err = noErr;
  769.  
  770.     err = CopyOneFork(source, dest, true);
  771.     if (err != noErr) return err;
  772.     return CopyOneFork(source, dest, false);
  773. }
  774.